58장. 사전 서명 URL과 권한 모델
이 장에서 말하고자 하는 것
사용자가 큰 파일(이미지 · 동영상) 을 업로드한다.
① 사용자 → 우리 서버 → S3
② 사용자 → S3 직접
①은 서버 트래픽과 컴퓨팅을 잡아먹는다.
②는 가장 효율적이지만 “어떻게 임시로 권한을 줄 것인가” 가 문제다.
이걸 푸는 방식이
사전 서명 URL (Presigned URL)
이다.
1. 사전 서명 URL이란
서버가 S3에 대한 임시 권한이 담긴 URL을 만들어준다.
https://msa-uploads.s3.amazonaws.com/users/123/profile.png?
X-Amz-Algorithm=...
&X-Amz-Credential=...
&X-Amz-Expires=300
&X-Amz-Signature=...
- 5분간만 유효
- 이 객체에 대해 PUT (또는 GET) 만 허용
- URL을 가진 사람은 그 동안만 직접 S3 접근 가능
2. 흐름
1. 사용자: "프로필 사진 올릴 거야"
2. 서버: 사전 서명 URL 생성 → 사용자에게 전달
3. 사용자 → 그 URL로 S3에 직접 PUT
4. 서버: (선택) S3 이벤트로 후처리
서버는 데이터 자체를 들고 다니지 않는다.
- 트래픽 절감
- 서버 부하 절감
- 보안: 임시 권한이라 노출 위험 적음
3. PUT 용 / GET 용
GET 용: 비공개 객체를 잠시 다운로드 가능하게
PUT 용: 임시 업로드 슬롯
GET용은 “private 영상 일시 재생” 같은 용도에 쓴다.
4. POST Policy — 더 정교한 업로드 제어
PUT 사전 서명 URL은 한 객체에 대한 단순 권한이다.
업로드 시 제약 (크기 · Content-Type · 접두사) 이 필요하면 POST Policy 를 쓴다.
허용 조건:
- 키가 uploads/user-{id}/ 로 시작
- Content-Type은 image/*
- 크기 5MB 이하
위반하는 업로드는 S3가 자체적으로 거부한다.
5. S3 권한 모델 — 세 층
S3 접근은 세 층의 검사를 모두 통과해야 한다.
1. IAM 정책 (호출자가 무엇을 할 수 있는가)
2. 버킷 정책 (이 버킷이 누구에게 무엇을 허용)
3. 객체 ACL (개별 객체 권한 — 거의 안 쓴다)
그리고 Public Access Block 으로 전체 노출이 한 번 더 막힌다.
기본은 모든 게 막혀 있고, 세 층 중 하나라도 거절하면 통과 안 된다.
6. CloudFront OAC와 사전 서명 URL의 차이
| 항목 | OAC (CloudFront) | 사전 서명 URL |
|---|---|---|
| 대상 | 모든 사용자 | 특정 사용자 |
| 캐시 | 가능 | 거의 불가 |
| 만료 | 없음 | 짧음 (분 단위) |
| 용도 | 정적 공개 자원 | 비공개 / 일시 접근 |
정적 공개 → CloudFront + OAC
사용자별 비공개 → 사전 서명 URL
7. 우리 서비스에서
[사용자] (프로필 사진 업로드)
↓ POST /api/uploads/presign
[API Gateway → ECS "uploads"]
↓ Task Role로 S3 PutObject 권한
↓ presigned URL 생성
[사용자] (URL 받음)
↓ PUT 그 URL (S3 직접)
[S3 버킷 uploads]
↓ S3 이벤트
[Lambda 또는 SQS] (썸네일 생성 등)
uploads 서비스는 데이터를 만지지 않는다. “URL 발급기” 역할만.
8. 직접 확인해보기 — CLI
aws s3 presign s3://msa-uploads/users/123/profile.png --expires-in 300
--expires-in 단위는 초.
curl --upload-file ./file.png "<presigned-url>"
로 PUT 하면 파일이 올라간다.
9. 코드로는 이렇게 생겼다 — Node.js
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const s3 = new S3Client({ region: "ap-northeast-2" });
async function presign(key) {
const command = new PutObjectCommand({
Bucket: "msa-uploads",
Key: key,
ContentType: "image/png",
});
return getSignedUrl(s3, command, { expiresIn: 300 }); // 5분
}
ECS Task Role이 s3:PutObject 권한을 가지면 끝.
10. 이렇게 쓰면 망한다 — 안티패턴
안티패턴 1. 만료 시간을 길게 둔다
URL이 새어 나가면 그 시간 동안 누구나 업로드 가능.
분 단위로 짧게 — 5~15분 정도
안티패턴 2. 큰 파일을 서버를 통해 받는다
API Gateway는 페이로드 크기 제한이 있다. ECS도 큰 파일은 메모리 부담.
큰 파일은 사전 서명 URL로 사용자 → S3 직접
안티패턴 3. POST Policy 제약을 안 건다
악성 사용자가 거대한 파일을 올려 비용 폭탄을 만들 수 있다.
안티패턴 4. 사전 서명 URL을 CloudFront에 노출한다
캐시되면 만료된 URL이 계속 응답된다.
11. 한 줄로 정리
사전 서명 URL은 사용자 ↔ S3 직접 통신에 짧은 임시 권한을 주는 방식이며,
큰 파일 업로드 · 비공개 자원 접근의 표준 도구다
12. 이 장의 핵심 정리
- 사전 서명 URL은 임시 S3 접근 권한이 담긴 URL이다.
- 사용자 → S3 직접 통신으로 서버 부담을 없앤다.
- PUT · GET 둘 다 가능하다.
- 제약이 필요하면 POST Policy.
- 만료는 짧게 — 보통 5~15분.
- CloudFront OAC와는 용도가 다르다 — 공개 vs 일시 비공개.